﻿using UnityEngine;
using System.Collections.Generic;
using TTGame;

/*
 * MagicBall
 * 法球，或者说子弹
 *
 *
 *
 * 子弹需要发射器才能发射出去。
 * 子弹的发射方法中，不包括设置自己的初始坐标。因为这些参数是发射器去控制的。
 *
 * 为了简便单个法球的发射，添加EmitSelf方法，即自己把自己发射出去。
 *
 * 有触发器，
 * 维护对象列表，防止重复伤害。如果有触发器，那么列表有触发器维护。
 *
 * 重要：
 * 子弹预设 = 子弹脚本+特效节点
 *
 * 子弹流程
 * 1. ObjectManager.CreateMBWithPrefab()创建子弹对象。初始子弹属性、添加碰撞脚本。
 * 2. 发射 EmitSelf/Fire
 * 3. 由MagicBall控制自己的生命、飞行轨迹
 * 4. 碰撞回调触发。处理伤害、击中效果。
 * 5. 销毁子弹
 *
 *
 * mark:youhua
 * 想做的是，子弹的各个属性和逻辑通过组合模块的方式搭建。最好用统一一个类来组装并处理逻辑。
 * 如：
 * 子弹由以下参数模块和逻辑组合。运动轨迹模块（参数和逻辑），生命周期模块，碰撞模块，子弹发射模块（发射多个、发射间隔，似乎不应该子弹来处理？）等。
 *
 * 依赖：
 * MagicBallEmitter
 *
 * ！不写重复代码！
 * @author TTun
 * @date 7/18/2017 8:06:12 PM
 */
namespace TTGameEngine {

public delegate void OnMagicBallDeactive(MagicBall mb);

public class MagicBall : MonoBehaviour {

    static public bool MagicBallLogFlag = true;

    protected MagicBallEmitter m_emitter;
    [SerializeField, ReadOnly]
    protected Puppet m_owner;
    protected bool m_isActive = true;
    protected uint m_skillCastID;     //隶属于的技能施法ID
    protected int m_skillID;     //隶属于的技能ID
    protected uint m_bulletGID = 0;    //全局唯一标识
    protected static CountID_UInt BulletGlobalID = new CountID_UInt();
    [SerializeField]
    protected float m_lifeTime;       //持续时间
    [SerializeField]
    protected int m_maxCollideCount;            //次数为0后，将销毁子弹。m_maxCollideCount=1,表示只碰撞一个对象，即子弹不穿透。如果想要“无限”，填大一点的数字吧。

    [SerializeField]
    protected ColliderInfo m_colInfo;

    float t_lifeTime;
    protected int t_lastCollideCount;
    [SerializeField]
    bool IsDestroyAfterCollider = true;
    protected DamageHandler m_damageHandler;
    protected DamageReceiverCollider drCollider;
    [SerializeField, Tooltip("子弹消耗")]
    public int m_bulletConsume = 1;

    protected Transform xform;
    protected bool Verify_hasInit = false; //用于自检。防止忘记调用基类的init
    [SerializeField, ReadOnly]
    bool m_isNoHarm = false;
    protected bool targetObjIsSelf = false;
    int effectLayerID = 0;

    //伤害信息---------
    float skillDmgFactor = 1f;
    float skillDmgAdd = 0;
    float critRate = 0f;
    float fetterAtkRate = 1f;
    [SerializeField]
    protected List<int> m_buffIDs;


    public event OnMagicBallDeactive CB_MagicBallDeactive;

    //二级子弹---------
    [SerializeField, Tooltip("二级子弹")]
    string followUpBullet;
    SkillHandler m_skillHandler;

    protected virtual void Awake() {
        enabled = false;
    }

    // Use this for initialization
    protected virtual void Start() {

    }

    // Update is called once per frame
    protected virtual void Update() {
        float dt = Time.deltaTime;
        bool result = true;
        result &= updateLife(dt);
        if(result) {
            result &= updateColliderCount();
        }

        if(result == false) {
            OnLifeOver();
        }
    }

    protected virtual void InitWithLocalParams(uint skillCastID) {

    }

    protected virtual void init(int skillID, uint skillCastID) {
        targetObjIsSelf = false;
        Verify_hasInit = true;
        m_skillID = skillID;
        m_skillCastID = skillCastID;
        xform = transform;
        t_lastCollideCount = m_maxCollideCount;
        m_isActive = true;
        if(m_buffIDs != null) m_buffIDs.Clear();
        t_lifeTime = m_lifeTime;
        //最快以30帧为准。太快的话，检测长度太短了。
        m_colInfo.m_moveSpeed = GetMoveSpeed();
        effectLayerID = 0;
        GetGID();
    }
    public virtual MagicBall Init(Puppet ownerObj, int skillID, uint castID, float lifeTime, TargetTypeID targetType) {
        m_owner = ownerObj;
        //碰撞其他信息使用本地参数。
        m_colInfo.m_targetType = targetType;
        m_lifeTime = lifeTime;
        init(skillID, castID);
        return this;
    }

    public virtual MagicBall Init(Puppet ownerObj, int skillID, uint castID, float lifeTime, ColliderInfo colInfo) {
        m_owner = ownerObj;
        m_lifeTime = lifeTime;
        m_colInfo = colInfo;
        init(skillID, castID);
        return this;
    }

    public uint GetGID() {
        if(m_bulletGID == 0) {
            m_bulletGID = BulletGlobalID.GetGID();
        }
        return m_bulletGID;
    }

    public void InitDamageInfo(float aSkillDmgFactor, float aSkillDmgAdd, float aCritRate, float aFetterAtkRate) {
        skillDmgFactor = aSkillDmgFactor;
        skillDmgAdd = aSkillDmgAdd;
        critRate = aCritRate;
        fetterAtkRate = aFetterAtkRate;
    }

    #region 设置参数
    public void SetIsNoHarm(bool b) {
        m_isNoHarm = b;
    }

    public void SetEffectLayerID(int layerID) {
        effectLayerID = layerID;
        this.gameObject.SetLayerInChildren(layerID);
    }

    public void SetMaxColliderCount(int maxCount) {
        m_maxCollideCount = maxCount;
        t_lastCollideCount = m_maxCollideCount;
    }

    public void SetColliderTargetStrategy(TargetStrategy strategy) {
        if(drCollider) drCollider.m_targetStrategy = strategy;
    }

    public void SetFollowUpBullet(string aFollowUpBullet, SkillHandler skillHandler) {
        followUpBullet = aFollowUpBullet;
        m_skillHandler = skillHandler;
    }

    public void AddBuff(int buffID) {
        if(m_buffIDs == null) m_buffIDs = new List<int>();
        m_buffIDs.Add(buffID);
    }
    public void AddBuff(List<int> buffID) {
        if(m_buffIDs == null) m_buffIDs = new List<int>();
        m_buffIDs.AddRange(buffID);
    }
    #endregion

    protected float lastMoveDis = 0;

    /// <summary>
    ///
    /// </summary>
    /// <param name="dt"></param>
    /// <returns>false，子弹结束，应当销毁</returns>
    protected virtual bool updateLife(float dt) {
        t_lifeTime -= dt;
        //至少等【检测】一次碰撞后再取消。以免生命时间错误的填成0 ，导致子弹一产生就消失。
        if(t_lifeTime <= 0 && (drCollider == null || drCollider.ColliderCheckCount > 0)) {
            t_lifeTime = 0;
#if UNITY_EDITOR
            if(m_colInfo.m_atkShape == ColliderShape.Line) {
                MagicBallLogWarning("子弹生命结束，没有碰撞到怪物。 speed[{0}] rayLenFactor[{1}]", m_colInfo.m_moveSpeed, m_colInfo.m_rayLenFactor);
            }
#endif
            return false;
        }
        return true;
    }

    /// <summary>
    ///
    /// </summary>
    /// <returns>false，子弹结束，应当销毁</returns>
    protected virtual bool updateColliderCount() {
        if(t_lastCollideCount <= 0 && IsDestroyAfterCollider) {
            return false;
        }
        return true;
    }

    protected virtual void OnLifeOver() {
        DeactiveMagicBall();
    }

    //用于自己发射自己时。
    public virtual void EmitSelf() {
        //1、设置坐标等
        //2、发射
        Fire();
    }
    public virtual void EmitSelf(Vector3 startPos) {
        transform.parent = MagicBallManager.Share().GetRootTF();
        transform.position = startPos;
        EmitSelf();
    }

    void CheckParams() {
        if(Verify_hasInit == false) {
            MagicBallLogError("Verify_hasInit Error! Magic没有初始化。没有调用基类MagicBall的Init()");
        }
        if(targetObjIsSelf == false && drCollider == null) {
            MagicBallLogError("碰撞框Null");
        }
    }

    public virtual void SetMoveSpeed(float moveSpeed) {
    }

    public virtual float GetMoveSpeed() {
        return 0;
    }

    //用于发射器调用发射时。
    public virtual void Fire() {
        MagicBallLog("[MagicBall] fire bullet.");

        CheckParams();
        enabled = true;

        if(targetObjIsSelf) {
            _onCollider(m_owner, xform.position);
            OnHitEffect(m_owner, this.tag, xform.position + new Vector3(0, m_owner.HitPosHeight, 0), xform.forward);
        } else {
            //碰撞框一出现，就立马检测一次，以免出现子弹速度太快，下一帧就在远处的情况。
            if(drCollider) drCollider.BeginningColliderRay(m_owner.Pos);
        }
    }

    //使法球失效
    public virtual void DeactiveMagicBall(bool isRemove = true) {
        MagicBallLog("[MagicBall] Recycle bullet.");
        if(CB_MagicBallDeactive != null) CB_MagicBallDeactive(this);
        enabled = false;
        m_isActive = false;
        Verify_hasInit = false;
        if(m_buffIDs != null) m_buffIDs.Clear();
        //停止逻辑
        //gameObject.RemoveComponentsSafe<DamageReceiverCollider>();
        //GameObject.Destroy(drCollider);
        drCollider = null;
        //可能需要等消逝动画结束才会真正的删除特效。
        //回收对象由MagicBallManager负责
        CB_MagicBallDeactive = null;
        m_skillHandler = null;
        m_isNoHarm = false;
        ObjectRecycler.Instance.Deactivate(gameObject);
        if(isRemove) MagicBallManager.Share().RemoveMagicBall(this);
        m_bulletGID = 0;
    }

    public void SetDamageHandler(DamageHandler dmgHandler) {
        m_damageHandler = dmgHandler;
    }

    protected void ApplyDamage(Puppet reciverObj, DamageInfo dmgInfo) {
        if(m_damageHandler != null) {
            m_damageHandler(reciverObj, dmgInfo);
        } else {
            MagicBallLogError("Need RegistDamageHandler in bulletTriger");
            reciverObj.ApplyDamage(dmgInfo);
        }
    }

    protected virtual bool OnCollider(Puppet reciverObj, DamageReceiverCollider col) {
        return _onCollider(reciverObj, col.transform.position);
    }

    protected bool _onCollider(Puppet reciverObj, Vector3 hitPoint) {
        bool isColliderValid = false;
        if(false == enabled || false == m_isActive || t_lastCollideCount <= 0) {
            return isColliderValid;
        }
        t_lastCollideCount--;
        isColliderValid = true;
        if(m_isNoHarm && (m_buffIDs == null || m_buffIDs.Count == 0)) {
            return isColliderValid;
        }

        DamageInfo info = DamageInfo.CreateDamageInfo(m_owner, reciverObj, m_skillID, m_skillCastID, skillDmgFactor, skillDmgAdd, critRate, fetterAtkRate, DamageSource.Skill);
        info.hitPoint = hitPoint;
        info.hitPointOffset = info.hitPoint - reciverObj.objTF.position;
        info.IsNoHarm = m_isNoHarm;

        if(m_buffIDs != null) {
            for(int i = 0; i < m_buffIDs.Count; i++) {
                int buffID = m_buffIDs[i];
                info.buffIDs.Add(m_buffIDs[i]);
            }
        }
        ApplyDamage(reciverObj, info);


        return isColliderValid;
    }

    // 击中特效处理（被击中的目标，目标tag ， 击中中 ， 击中点法向量）
    protected virtual void OnHitEffect(Puppet reciverObj, string hittag, Vector3 hitPoint, Vector3 normal) {
        if(string.IsNullOrEmpty(followUpBullet) == false) {
            MagicBall bullet = MagicBallManager.Share().CreateMBWithPrefab(followUpBullet);
            if(bullet != null) {
                //使用上一子弹的信息。没有设置移动数据，假设原地爆炸
                bullet.Init(m_owner, m_skillID, m_skillCastID, bullet.m_lifeTime, m_colInfo.m_targetType);
                bullet.SetMoveSpeed(0);
                bullet.InitDamageInfo(skillDmgFactor, skillDmgAdd, critRate, fetterAtkRate);
                bullet.SetEffectLayerID(effectLayerID);
                if(drCollider) bullet.SetColliderTargetStrategy(drCollider.m_targetStrategy);
                bullet.AddBuff(m_buffIDs);
                if(m_skillHandler != null) m_skillHandler.RegistBullet(bullet);
                bullet.EmitSelf(hitPoint);
            }
        }

        BulletEffectScript.Ins.PlayEffect(tag, hitPoint, reciverObj, effectLayerID);
    }

    public void MagicBallLog(string msgformat, params object[] args) {
        if(MagicBallLogFlag == false) {
            return;
        }
        DebugUtil.LogFormat("[MagicBall]obj:[{0}]bullet:[{1}] {2}  ", m_owner != null ? m_owner.GetGID() : "no obj", gameObject.name, string.Format(msgformat, args));
    }
    public void MagicBallLogWarning(string msgformat, params object[] args) {
        if(MagicBallLogFlag == false) {
            return;
        }
        DebugUtil.LogWarningFormat("[MagicBall]obj:[{0}]bullet:[{1}] {2}  ", m_owner != null ? m_owner.GetGID() : "no obj", gameObject.name, string.Format(msgformat, args));
    }
    public void MagicBallLogError(string msgformat, params object[] args) {
        DebugUtil.LogErrorFormat("[MagicBall]obj:[{0}]bullet:[{1}] {2}  ", m_owner != null ? m_owner.GetGID() : "no obj", gameObject.name, string.Format(msgformat, args));
    }

#if UNITY_EDITOR
    protected void OnDrawGizmos() {
        if(Application.isPlaying == true) {
            return;
        }
        Gizmos.color = Color.green;

        Tools.DrawColliderInfo(this.transform, m_colInfo);
    }
#endif
}
}